package com.kgc.scd.interceptor;

import com.alibaba.fastjson.JSON;
import com.kgc.scd.annotation.RequestPermission;
import com.kgc.scd.util.RedisUtils;
import com.kgc.scd.util.ResultBuildUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @program: com.kgc.springcloud.interceptor
 * @author: HarryXzy
 * @create: 2022-06-01 10:16
 * @description: 自定义请求许可拦截器
 **/
@Slf4j
public class RequestPermissionInterceptor implements HandlerInterceptor {

    @Autowired
    private RedisUtils redisUtils;

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception {
        // 判断是否需要进行请求许可权限校验(看目标请求处理方法上有没有添加自定义请求许可注解-@RequestPermission)
        if(this.checkTargetMethodHadRequestPermission(handler)){
            // 需要鉴权，从请求头中获取token参数，判断是否已经登陆鉴权通过
            String userToken = httpServletRequest.getHeader("token");

            // 判断token参数是否为空，如果为空，鉴权失败，重新登陆
            if(StringUtils.isEmpty(userToken)){
                log.warn("**** token参数为空，统一鉴权失败 ****");
                this.returnCheckJson(httpServletResponse,"301","token参数为空，鉴权失败");
                // 拦截请求
                return false;
            }

            // 判断token的参数是否有效，如果有效，鉴权通过，否则鉴权失败，重新登陆
            if(ObjectUtils.isEmpty(redisUtils.get(userToken))){
                log.warn("**** token参数失效，统一鉴权失败！ ****");
                this.returnCheckJson(httpServletResponse,"302","token失效，鉴权失败");
                return false;
            }
        }
        // 请求方向（没有请求许可注解，不需要鉴权，放行，或者是有请求许可注解，鉴权通过，也要放行，请求发送到目标请求处理方法）
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }

    /**
    * @author: HarryXzy
    * @date: 2022/6/1 10:45
    * @return: boolean
    * @description: 判断目标请求处理方法上，是否有@RequestPermission注解，如果加了，返回true需要权限校验，否则返回false
    */
    private boolean checkTargetMethodHadRequestPermission(Object handler){
        // 判断当前handler对象是否已经映射到了目标请求处理方法（是否是HandlerMethod实例对象）
        if(handler instanceof HandlerMethod){
            // 强转为目标请求处理对象（HandlerMethod封装了目标请求处理方法的所有内容，包括方法声明注解）
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            // 尝试获取目标请求处理方法上的指定注解@RequestPermission
            RequestPermission requestPermission = handlerMethod.getMethod().getAnnotation(RequestPermission.class);

            // 判断目标方法上获取的请求注释是否存在，如果不存在，不代表就不需要校验，可能此注解添加在类上
            if(null == requestPermission){
                 requestPermission = handlerMethod.getMethod().getDeclaringClass().getAnnotation(RequestPermission.class);
            }

            // 如果获取到，说明需要进行请求许可鉴权，如果获取不到，说明不需要鉴权
            return null != requestPermission;
        }
        // 不需要请求许可鉴权，直接返回false
        return false;
    }

    /**
    * @author: HarryXzy
    * @date: 2022/6/1 10:59
    * @return: void
    * @description: token鉴权失败，统一返回结果
    */
    private void returnCheckJson(HttpServletResponse response, String returnCode, String returnMsg){
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json; charset=utf-8");
        try {
            response.getWriter().print(JSON.toJSONString(ResultBuildUtil.fail(returnCode, returnMsg)));
        } catch (IOException e) {
            log.error("响应结果异常:{}",e.getMessage());
        }
    }
}
